//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <bulverde.h>
#include <INTCBits.h>
#include "drvpub.h"

#define IRQ_GPIO     10
#define GPIO_PINS    121

typedef struct {
    int  PinNumber;
    void * Handler;
    void * Device;
} GpioPin;

int g_gpioPinUsed = 0;
GpioPin g_gpioPins[GPIO_PINS];

static Boolean ValidatePin(long Pin)
{
    // Pin0, Pin1: Not use irq number IRQ_GPIO
    // Pin2, Pin5~8: Special use
    if (Pin == 3 || Pin == 4) return TRUE;
    if (Pin > 8 && Pin  < GPIO_PINS) return TRUE;
    return FALSE;
}

EXTERN_C Boolean Drv_GpioInstall(uint_t Pin, void *Device, void * Handler)
{
    if (!ValidatePin(Pin)) return FALSE;

    g_gpioPins[g_gpioPinUsed].PinNumber = (int)Pin;
    g_gpioPins[g_gpioPinUsed].Handler = Handler;
    g_gpioPins[g_gpioPinUsed].Device = Device;

    g_gpioPinUsed++;

    return TRUE;
}

INLINE ulong_t GetGedrValue(long RegIdx)
{
    ulong_t GedrValue;

    Drv_Assert(RegIdx >=0 && RegIdx < 4);
    switch (RegIdx) {
    case 0:
        GedrValue = GEDR0;
        break;
    case 1:
        GedrValue = GEDR1;
        break;
    case 2:
        GedrValue = GEDR2;
        break;
    default: // case 3:
        GedrValue = GEDR3;
        break;
    }

    return GedrValue;
}

// Interrupt routine for GPIO
//static Boolean GpioIsr(void *pThis, Boolean * pWakeup)
void CDECL GpioIsr(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    long i, PinNumber;
    Boolean bDispatched = FALSE;

    for (i = 0; i < g_gpioPinUsed; i++) {
        long RegIndex, BitIndex;
        unsigned long GedrValue;
        typedef Boolean (*IsrHandler)(void *, Boolean *);

        PinNumber = g_gpioPins[i].PinNumber;
        RegIndex = (PinNumber >> 5) & 0x3;
        BitIndex = PinNumber & 0x1F; // The bit int the corresponding GEDR

        GedrValue = GetGedrValue(RegIndex);
        if ((GedrValue & (1 << BitIndex)) && g_gpioPins[i].Handler) {
            ((IsrHandler)g_gpioPins[i].Handler)(g_gpioPins[i].Device, NULL);
            bDispatched = TRUE;
            // Clear GPIO Status by the driver itself
        }

    }

    if (!bDispatched) {
        kprintf("Unknown ISR for GPIO!\n");
        kprintf("Dump GEDR: 0x%08x 0x%08x 0x%08x 0x%08x\n", GEDR0, GEDR1, GEDR2, GEDR3);
    }

//    INTC_GPIOXX_2_INT_EN(ICMR);

}

// Add the GPIO device to system
void GpioInit()
{
    long i;

    DzRegisterIsr(IRQ_GPIO, IPL7, (isr_t)GpioIsr, NULL);


    for (i = 0; i < GPIO_PINS; i++) {
        g_gpioPins[i].PinNumber = -1;
        g_gpioPins[i].Handler = NULL;
        g_gpioPins[i].Device = NULL;
    }

    return;
}


class CGPIO : public CDeviceDriver {
public:
    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);
    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & ebbInData,
        /* [out] */ MemoryBuf * pOutData,
        /* [out] */ IEvent * * ppCompletionEvent);
    virtual void Dispose();

};

CGPIO s_dummyGPIO;

ECode CGPIO::Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CGPIO::Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CGPIO::Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

void CGPIO::Dispose()
{
    return;
}

EXTERN IDeviceDriver * CDECL CreateGPIO(uint_t uDeviceNo, void *pvParameter)
{
    GpioInit();

    return &s_dummyGPIO;
}
